package utils

import (
	"context"
	"fmt"
	"strconv"

	"github.com/flipped-aurora/gin-vue-admin/server/global"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

// CommunityRedis 社区Redis操作工具类
type CommunityRedis struct {
	Context context.Context
}

// NewCommunityRedis 创建社区Redis工具实例
func NewCommunityRedis() *CommunityRedis {
	return &CommunityRedis{
		Context: context.Background(),
	}
}

// GetDisciplineCommunityZSetKey 获取学科类社区zset的key
// 现在支持根据社区ID和内容类型两个维度维护缓存
func GetDisciplineCommunityZSetKey(communityID uint, contentType global.ContentType) string {
	var redisContentType int
	switch contentType {
	case global.ContentTypeDynamics:
		redisContentType = int(global.ContentTypeDynamics)
	case global.ContentTypeQuestions:
		redisContentType = int(global.ContentTypeQuestions)
	case global.ContentTypeAnswers:
		redisContentType = int(global.ContentTypeAnswers)
	default:
		redisContentType = int(global.ContentTypeDefault)
	}
	return fmt.Sprintf("community:discipline:%d:content:%d", communityID, redisContentType)
}

// AddContentToDisciplineZSet 将内容添加到学科类社区的zset中
// disciplineID: 学科类社区ID
// contentID: 内容ID
// contentType: 内容类型
// timestamp: 内容发布时间戳(秒)
func (cr *CommunityRedis) AddContentToDisciplineZSet(disciplineID uint, contentID int64, contentType global.ContentType, timestamp int64) error {
	// 如果不是默认类型，先处理特定类型的缓存
	if contentType != global.ContentTypeDefault {
		// 处理特定类型的缓存
		specificKey := GetDisciplineCommunityZSetKey(disciplineID, contentType)
		err := global.GVA_REDIS.ZAdd(
			cr.Context,
			specificKey,
			redis.Z{
				Score:  float64(timestamp),
				Member: contentID,
			},
		).Err()

		if err != nil {
			global.GVA_LOG.Error("Add content to discipline community specific zset failed",
				zap.Uint("disciplineID", disciplineID),
				zap.Int64("contentID", contentID),
				zap.Int("contentType", int(contentType)),
				zap.Error(err))
			return err
		}

		// 检查当前特定类型zset的长度
		currentLen, err := global.GVA_REDIS.ZCard(cr.Context, specificKey).Result()
		if err != nil {
			global.GVA_LOG.Error("Get discipline community specific zset length failed",
				zap.Uint("disciplineID", disciplineID),
				zap.Int("contentType", int(contentType)),
				zap.Error(err))
			return err
		}

		// 如果超过限制长度，需要删除多余元素并同步到默认类型缓存
		const maxLength = 100
		if currentLen > maxLength {
			// 计算需要删除的元素数量
			delCount := currentLen - maxLength

			// 先获取将要被删除的元素（最早的元素）
			removedMembers, err := global.GVA_REDIS.ZRange(cr.Context, specificKey, 0, delCount-1).Result()
			if err != nil {
				global.GVA_LOG.Error("Get members to be removed from discipline community specific zset failed",
					zap.Uint("disciplineID", disciplineID),
					zap.Int("contentType", int(contentType)),
					zap.Error(err))
				return err
			}

			// 从特定类型缓存中删除多余元素
			_, err = global.GVA_REDIS.ZRemRangeByRank(cr.Context, specificKey, 0, delCount-1).Result()
			if err != nil {
				global.GVA_LOG.Error("Trim discipline community specific zset failed",
					zap.Uint("disciplineID", disciplineID),
					zap.Int("contentType", int(contentType)),
					zap.Error(err))
				return err
			}

			// 从默认类型缓存中删除这些被溢出的元素
			defaultKey := GetDisciplineCommunityZSetKey(disciplineID, global.ContentTypeDefault)
			for _, member := range removedMembers {
				// 将字符串转换为int64
				removedContentID, err := strconv.ParseInt(member, 10, 64)
				if err != nil {
					global.GVA_LOG.Error("Parse content ID failed",
						zap.String("member", member),
						zap.Error(err))
					continue
				}

				_, err = global.GVA_REDIS.ZRem(cr.Context, defaultKey, removedContentID).Result()
				if err != nil {
					global.GVA_LOG.Error("Remove overflow content from discipline community default zset failed",
						zap.Uint("disciplineID", disciplineID),
						zap.Int64("contentID", removedContentID),
						zap.Error(err))
				}
			}
		}
	}

	// 同时添加到默认类型(type=0)的缓存中
	defaultKey := GetDisciplineCommunityZSetKey(disciplineID, global.ContentTypeDefault)
	err := global.GVA_REDIS.ZAdd(
		cr.Context,
		defaultKey,
		redis.Z{
			Score:  float64(timestamp),
			Member: contentID,
		},
	).Err()

	if err != nil {
		global.GVA_LOG.Error("Add content to discipline community default zset failed",
			zap.Uint("disciplineID", disciplineID),
			zap.Int64("contentID", contentID),
			zap.Error(err))
		return err
	}

	// 默认类型zset长度不需要限制

	global.GVA_LOG.Debug("Add content to discipline community zset success",
		zap.Uint("disciplineID", disciplineID),
		zap.Int64("contentID", contentID),
		zap.Int("contentType", int(contentType)),
		zap.Int64("timestamp", timestamp))

	return nil
}

// RemoveContentFromDisciplineZSet 从学科类社区的zset中删除内容
func (cr *CommunityRedis) RemoveContentFromDisciplineZSet(disciplineID uint, contentID int64, contentType global.ContentType) error {
	// 如果不是默认类型，先处理特定类型的缓存
	if contentType != global.ContentTypeDefault {
		// 处理特定类型的缓存
		specificKey := GetDisciplineCommunityZSetKey(disciplineID, contentType)
		_, err := global.GVA_REDIS.ZRem(cr.Context, specificKey, contentID).Result()

		if err != nil {
			global.GVA_LOG.Error("Remove content from discipline community specific zset failed",
				zap.Uint("disciplineID", disciplineID),
				zap.Int64("contentID", contentID),
				zap.Int("contentType", int(contentType)),
				zap.Error(err))
			return err
		}
	}

	// 同时从默认类型(type=0)的缓存中删除
	defaultKey := GetDisciplineCommunityZSetKey(disciplineID, global.ContentTypeDefault)
	_, err := global.GVA_REDIS.ZRem(cr.Context, defaultKey, contentID).Result()

	if err != nil {
		global.GVA_LOG.Error("Remove content from discipline community default zset failed",
			zap.Uint("disciplineID", disciplineID),
			zap.Int64("contentID", contentID),
			zap.Int("contentType", int(contentType)),
			zap.Error(err))
		return err
	}

	global.GVA_LOG.Debug("Remove content from discipline community zset success",
		zap.Uint("disciplineID", disciplineID),
		zap.Int64("contentID", contentID),
		zap.Int("contentType", int(contentType)))

	return nil
}

// GetDisciplineCommunityContents 获取学科类社区的内容列表(按时间倒序)
func (cr *CommunityRedis) GetDisciplineCommunityContents(disciplineID uint, contentType global.ContentType) ([]int64, error) {
	key := GetDisciplineCommunityZSetKey(disciplineID, contentType)

	// 获取zset中的内容ID，按分数倒序排列
	result, err := global.GVA_REDIS.ZRevRange(cr.Context, key, 0, -1).Result()

	if err != nil {
		global.GVA_LOG.Error("Get discipline community contents failed",
			zap.Uint("disciplineID", disciplineID),
			zap.Int("contentType", int(contentType)),
			zap.Error(err))
		return nil, err
	}

	// 将结果转换为int64切片
	contentIDs := make([]int64, 0, len(result))
	for _, idStr := range result {
		id, err := strconv.ParseInt(idStr, 10, 64)
		if err != nil {
			continue
		}
		contentIDs = append(contentIDs, id)
	}

	return contentIDs, nil
}
